﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using Ue.Extensions;

namespace Ue.Components.VerificationCode
{
    public class VerifyCodeStringComponent : IVerifyCodeComponent
    {
        private char[] chars;
        private VerifyCodeComponentElement<Color> colours;
        private VerifyCodeComponentElement<FontFamily> fontFamilies;
        private IEnumerable<IVerifyCodeCharComponents> charComponents;
        private static Encoding coding = Encoding.GetEncoding("gb2312");
        
        public VerifyCodeStringComponent(IEnumerable<char> chars,IEnumerable<Color> colours,string[] fontNames,params IVerifyCodeCharComponents[] charComponents)
        {
            chars.ArgumentNullExceptionByNullOrZeroElement("chars");
            colours.ArgumentNullExceptionByNullOrZeroElement("colours.Elements");
            colours.ArgumentNullExceptionByNullOrZeroElement("fontFamilies.Elements");

            this.chars = chars.GetRandom(4).ToArray();
            this.colours = new VerifyCodeComponentElement<Color>(colours);
            this.fontFamilies = new VerifyCodeComponentElement<FontFamily>(VerifyCodeHolder.GetFontFamilies(fontNames));
            this.charComponents = charComponents;

            if (this.charComponents == null || this.charComponents.Count()==0)
            {
                var tempList= new List<IVerifyCodeCharComponents>(){
                    new VerifyCodeCharComponentOfDraw()
                };

                if (chars.ToString().IsInteger())
                {
                    tempList.Add(new VerifyCodeCharComponentOfFontStyle());
                }

                this.charComponents=tempList;
            }
            this.charComponents = this.charComponents.OrderBy(item => item.Priority);

        }

        public VerifyCodeComponentElement<Color> Colours
        {
            get{ return this.colours;}
        }

        public VerifyCodeComponentElement<FontFamily> FontFamilies
        {
            get{ return this.fontFamilies;}
        }

        public char[] Chars
        {
            get { return this.chars; }
        }

        #region IVerifyCodeComponent 成员

        public int Priority
        {
            get { return 30; }
        }

        public void Draw(ref VerifyImageDrawContext context)
        {
            var tmpBitmapWidth = context.Bitmap.Width / this.chars.Length;
            var xAxisCursor = 0;
            for (int i = 0; i < this.chars.Length; i++)
            {
                var c=chars[i];

                var fontSize = VerifyCodeHolder.GetRandom((int)(tmpBitmapWidth*0.7), tmpBitmapWidth);
                var font = new Font(this.fontFamilies.GetRandomElement(), this.GetFontSizeOfAdaptiveTextType(c,(float)fontSize));

                Brush brush = new SolidBrush(this.colours.GetRandomElement());
                var tmpBitmap = new Bitmap(this.GetFontSizeOfPixel(font), context.Bitmap.Height);
                var tmpGraphics = Graphics.FromImage(tmpBitmap);
                tmpGraphics.SmoothingMode = SmoothingMode.AntiAlias;
                tmpGraphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

                foreach (var item in this.charComponents)
                {
                    item.StringComponent = this;
                    item.Draw(ref tmpBitmap, ref tmpGraphics,ref font,ref brush, c);
                }

                context.Graphics.DrawImage(tmpBitmap, xAxisCursor, 0);

                //graphics.DrawString(c.ToString(), font, brush, xAxisCursor, VerifyCodeHolder.GetRandom(-2, 3));

                xAxisCursor += tmpBitmap.Width > tmpBitmapWidth
                    ? tmpBitmapWidth
                    : tmpBitmap.Width;

                tmpGraphics.Dispose();
                tmpGraphics.Dispose();
            }
        }

        #endregion

        /// <summary>
        /// 取得字体所占的像素大小，此方法只适合于VerifyCode模块
        /// </summary>
        /// <param name="font"></param>
        /// <returns></returns>
        public int GetFontSizeOfPixel(Font font)
        {
            return (int)(font.SizeInPoints * 1.4);
        }

        /// <summary>
        /// 以英文字体大小为标准，自动区分中文、英文、数字自动生成适应大小的fontSize
        /// </summary>
        /// <param name="fontSize">标准字体大小：即英文字体大小</param>
        /// <returns></returns>
        public float GetFontSizeOfAdaptiveTextType(char c, float fontSize)
        {
            var charByteCount = VerifyCodeStringComponent.coding.GetByteCount(c.ToString());

            return charByteCount == 1 ? fontSize : fontSize / 2;
        }
    }
}
